from threading import RLock, Condition, Thread
import datetime
import time


class Progress:
    def __init__(self, size=50, display_char='=', left_char='-'):
        self.size = size
        self.total = 0
        self.display_char = display_char
        self.left_char = left_char

        self._last_length = 0
        self._start_time = None

    def reset(self, total):
        self.total = total
        self._start_time = time.time()
        self._last_length = 0
        print()

    def print(self, current, msg=None):
        assert self._start_time is not None, '请先调用reset()方法'
        percent = current / self.total
        if percent == 0:
            seconds = ''
        else:
            seconds = round((time.time() - self._start_time) * (1 - percent) / percent)
            if seconds > 60:
                seconds = '[还剩%d分%d秒]' % (seconds//60, seconds % 60)
            else:
                seconds = '[还剩%d秒]' % seconds
        bar = self.display_char * round(percent * self.size)
        bar = bar + '[%d%%]' % round(percent*100) + self.left_char * (self.size - len(bar))
        dt = datetime.datetime.now()
        dt = dt.strftime('%H:%M:%S')
        bar = '\r%s %s%s%s' % (dt, bar, seconds, '' if msg is None else msg)
        length = len(bar)
        if length < self._last_length:
            bar = bar + ' ' * (self._last_length - length)
        self._last_length = length
        print(bar, end='')


def get_dt():
    dt = datetime.datetime.now()
    return dt.strftime('%m-%d %H:%M:%S.%f')


class Configurable:
    def set_single_config(self, config, name, type=None, required=False, attr_name=None):
        if attr_name is None:
            attr_name = name
        if name in config:
            value = config[name]
            if type is not None:
                value = type(value)
            setattr(self, attr_name, value)
        elif required:
            raise Exception('配置中没有找到必选的' + name)
        else:
            value = getattr(self, attr_name)
        return value

    def get_single_config(self, config, name, default):
        return config[name] if name in config else default

    def get_config(self):
        raise Exception('get_config() is not defined')

    def set_config(self, config):
        raise Exception('set_config() is not defined')

    @staticmethod
    def from_config(config):
        if 'class' in config:
            clazz = config['class']
        elif 'type' in config:
            clazz = config.type
        else:
            raise Exception('配置中没有发现属性class的定义: %s' % config)
        exec('import ' + clazz[:clazz.rfind('.')])
        obj = eval(clazz + '()')
        obj.set_config(config)
        return obj


class Equipment:
    def __init__(self):
        self.lock = RLock()
        self.status = 'ready'
        self.stopping = False

        self.allow_reentry = False

    def get_status(self):
        """
        获取管道的状态。管道的状态有3种：'ready', 'running', 'pause'
        """
        with self.lock:
            return self.status

    def get_lock(self):
        return self.lock

    def get_stopping(self):
        with self.lock:
            return self.stopping

    def start(self):
        """
        用线程安全的方式启动这个管道。该函数会调用函数_start()，用户应该重定义后者以实现管道的启动逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            if self.allow_reentry and self.status == 'running':
                return
            assert self.status == 'ready', '%s在就绪(ready)状态下才能启动, 当前状态: %s' % (self, self.status)
            self.stopping = False
            try:
                self._start()
            finally:
                self.status = 'running'

    def _start(self):
        pass

    def stop(self):
        """
        用线程安全的方式关闭这个管道。该函数会调用函数_stop()，用户应该重定义后者以实现管道的关闭逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            if self.allow_reentry and self.status == 'ready':
                return
            assert self.status in ('running', 'pause'), '%s只有在运行(running)或暂停(pause)状态下才能停止，当前状态: %s' % (
                self, self.status)
            self.stopping = True
            try:
                self._stop()
            finally:
                self.status = 'ready'

    def _stop(self):
        pass

    def pause(self):
        """
        用线程安全的方式暂停这个管道。该函数会调用函数_pause()，用户应该重定义后者以实现管道的暂停逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            if self.allow_reentry and self.status == 'pause':
                return
            assert self.status == 'running', '管道%s只有在运行(running)状态下才能暂停, 当前状态: %s' % (self.name, self.status)
            try:
                self._pause()
            finally:
                self.status = 'pause'

    def _pause(self):
        pass

    def resume(self):
        """
        用线程安全的方式恢复这个管道的运行。该函数会调用函数_resume()，用户应该重定义后者以实现管道的恢复运行逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            if self.allow_reentry and self.status == 'running':
                return
            assert self.status == 'pause', '管道%s只有在暂停(pause)状态下才能恢复, 当前状态: %s' % (self.name, self.status)
            try:
                self._resume()
            finally:
                self.status = 'running'

    def _resume(self):
        pass


class ConfigurableEquipment(Equipment, Configurable):
    pass


if __name__ == '__main__':
    print()
    p = Progress()
    p.reset(100)
    for i in range(100):
        time.sleep(0.5)
        p.print(i+1, '%d' % (i*10))